﻿package igz.fleaxy.util
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.net.LocalConnection;
	import flash.system.System;
	import flash.utils.describeType;
	
	/**
	 * @author Intelygenz | www.intelygenz.com
	 */
	public class ObjectUtil
	{

		public function ObjectUtil() {
			
		}

		static public function $Remove(p_obj:Object) : void {
			p_obj = null;
			$CleanMemory();
		}

		static public function $CleanMemory() : void {
         try { // >= FP10
            System.gc();
         }
         catch (p_error:*) {
            try {
               new LocalConnection().connect('foo');
               new LocalConnection().connect('foo');
            }
            catch (p_error2:*) { }
         }
		}

		/**
		 * Copia las propiedades de un objeto origen a otro objeto destino
		 * @param	p_from
		 * @param	p_to
		 */
		static public function $Merge(p_from:Object = null, p_to:Object = null, p_fillNulls:Boolean=true) : Object {
			if ( p_from != null && p_to != null ) {
				
// Classes
				var varList:XMLList = flash.utils.describeType(p_from)..variable;
				for (var i:int = 0; i < varList.length(); i++) {
					// TODO : Merge recursivo
					if ( p_fillNulls ||
					   (!p_fillNulls && p_from[varList[i].@name] != null )
					){
						p_to[varList[i].@name] = p_from[varList[i].@name];
					}
				}

// Objects
				for ( var p:String in p_from ) {
					if (String(p_from[p]) == "[object Object]") {
						if ( !p_to.hasOwnProperty(p) || (p_to.hasOwnProperty(p) && p_to[p] == null)) {
							p_to[p] = { };
						}
						$Merge(p_from[p], p_to[p], p_fillNulls);
					}
					else if ( String(p_from[p])!="undefined" ) {
						if ( p_fillNulls ||
						   (!p_fillNulls && p_from[p] != null )
						) {
							try {
								p_to[p] = p_from[p];
							} catch(p_error:Error) { }
						}
					}
				}
			}
			return p_to;
		}

		/**
		 * Devuelve un String que define el tipo de un miembro de una clase, permite especificar si se requiere el tipo super
		 * @param	p_className
		 * @param	p_memberName
		 * @param	p_super
		 * @return
		 */
		static public function $GetType(p_obj:Object, p_memberName:String) : String {
			// Es mucho más rápido que leer el XML del describeType y procesar el xml por cada campo
			try {
				p_obj[p_memberName] = "";
			}
			catch (p_err0:Error) {
				try {
					p_obj[p_memberName] = new Number();
					return "number";
				}
				catch (err1:Error){
					try {
						p_obj[p_memberName] = new Date();
						return "date";
					}
					catch (p_err2:Error) {
						try {
							p_obj[p_memberName] = [];
							return "array";
						}
						catch (p_err3:Error) {
							try {
								p_obj[p_memberName] = new Bitmap();
								return "bitmap";
							}
							catch (p_err4:Error) {
								try {
									p_obj[p_memberName] = new Function();
									return "function";
								}
								catch ( p_error:Error ){
									
								}
							}
						}
					}
				}
				
				
			}
			return typeof(p_obj[p_memberName]);
		}

		//static protected function _GetDescribeType(p_className:String, p_memberName:String, p_super:Boolean = false) : String {
/*
			//var dtoClass:Class = getDefinitionByName(p_className) as Class;
			var classXml:XML = describeType(p_className);

			var classVariables:XMLList = classXml.factory.elements("variable");
			var classXmlNode:XML;
			var classType:String;
			var f:int = 0, F:int = classVariables.length();
			while ( f < F && classType == null ) {
				if ( classVariables[f].attribute("name") == p_memberName  ) {
					classType = classVariables[f].attribute("type");
				}
				f++;
			}

			if ( p_super ) {
				var refClass : Class = getDefinitionByName(classType) as Class;
				var obj : Object = new refClass();
				classType = getQualifiedSuperclassName(obj);
			}

			return classType;
		}
*/
		

		/**
		 * Comprueba para un objecto si el punto pasado como par�metro est� encima de un pixel transparente o no.
		 * @param	p_object Objeto que queremos comprobar
		 * @param	p_point Punto GLOBAL ( del stage ) que queremos comprobar
		 * @return  
		 */
		static public function $CheckTransparency( p_object : DisplayObject, p_point : Point ) : Boolean {
			//Si nos pasan un bitmap, podemos usar la funcion por defecto
			if( p_object is BitmapData ) {
				return (p_object as BitmapData).hitTest(new Point(0,0), 0, p_object.globalToLocal( p_point ));
			} else {
				/* Primero miramos si el metodo hitTestPoing devuelve false. Si lo hace, quiere decir que no tenemos
				 * un contacto asi que devovemos false. Pero si devuelve true, aun tenemos que comprobar que no es un contacto
				 * en un pixel transparente
				 */
				if(!p_object.hitTestPoint(p_point.x, p_point.y, true)) {
					return false;
				}
				else {
					/* Hacemos un nuevo BitmapData y dibujamos los pixeles del objecto. Con el bitmap data ya podemos usar
					 * el metodo hitTest del Bitmap para determinar si hay o no contacto 
					 */
					var bmapData:BitmapData = new BitmapData( p_object.width,  p_object.height, true, 0x00000000);
					bmapData.draw( p_object, new Matrix());
					
					var returnVal:Boolean = bmapData.hitTest(new Point(0,0), 0, p_object.globalToLocal(p_point));
					
					bmapData.dispose();
					
					return returnVal;
				}
			}
		}
		
		/*
		//Calcula la longitud de un object
		static public function $GetLength(object:Object) : uint
		{
			var length:uint = 0;
			for (var item:* in object)
				if (item != "mx_internal_uid")
					length++;
			return length;
		}
		*/
		
		

		/*
		 * Dado un object, vuelca los datos contenidos en el mismo con el siguiente formato:
		 *   etiqueta : valor (tipoDeDato)
		 */
		static public function $DebugObject( p_object: Object, p_indent:Number=0 ): void {
			var f:uint, F:uint = p_indent;
			var indent : String = "";
			for ( f = 0; f < F; f++ ) {
				indent += "\t";
			}
			
			var tag:Object;
			for ( tag in p_object ) {
				trace(indent + "  " + tag + " : " + p_object[tag]);
				if ( String(p_object[tag]) == "[object Object]" ) {
					$DebugObject(p_object[tag], p_indent+1);
				}
				
			}
		}
		
	}	
}